/******************************************************************************
Project    : Real time rtsp server library base on live555
*******************************************************************************
File Name    :  <VideoRTimeServerMediaSubsession.cpp>:  video real time SMSS source file
Module Name  :  
Description  :  None
History      :
 No.   Ver.     Date        Designed by  Comment
-----+--------+----------+------------+---------------------------------------
 001   0.00.01  06/05/2015  <3G@LIURENHUA>   <video real time SMSS source file>
*******************************************************************************/
#include <pthread.h>
#include "VideoRTimeFramedSource.h"
#include "VideoRTimeServerMediaSubsession.h"
//#include <utils/Log.h>

/******************************************************************************
  global variable declaration
 ******************************************************************************/
#ifdef LOCAL_SOCK_INPUT
pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER;
int gSMSSCnt = 0;
#endif /* LOCAL_SOCK_INPUT */

/******************************************************************************
  class functions define
 ******************************************************************************/
VideoRTimeServerMediaSubsession *VideoRTimeServerMediaSubsession::createNew(
							UsageEnvironment & env, 
							const tEsFmt eCodecId,
#ifdef LOCAL_SOCK_INPUT
							const char  *pcIdentity,
#endif /* LOCAL_SOCK_INPUT */
							const u_int8_t ucFrmRate,
#ifdef LOCAL_SOCK_INPUT
							const u_int32_t unSBufSize, 
							const u_int32_t unRBufSize
#else
							queue_t *pstQue
#endif /* LOCAL_SOCK_INPUT */
							)
{
	return new VideoRTimeServerMediaSubsession(env,
							eCodecId, 
#ifdef LOCAL_SOCK_INPUT
							pcIdentity,
#endif /* LOCAL_SOCK_INPUT */
							ucFrmRate,
#ifdef LOCAL_SOCK_INPUT
							unSBufSize,
							unRBufSize
#else
							pstQue
#endif /* LOCAL_SOCK_INPUT */
							);
}

VideoRTimeServerMediaSubsession::VideoRTimeServerMediaSubsession(
							UsageEnvironment & env,
							const tEsFmt eCodecId,
#ifdef LOCAL_SOCK_INPUT
							const char  *pcIdentity,
#endif /* LOCAL_SOCK_INPUT */
							const u_int8_t ucFrmRate,
#ifdef LOCAL_SOCK_INPUT
							const u_int32_t unSBufSize, 
							const u_int32_t unRBufSize
#else
							queue_t *pstQue
#endif /* LOCAL_SOCK_INPUT */
							) : OnDemandServerMediaSubsession(env, True)
{
    //ALOGD("%s:Enter ",__func__);
	m_eCodecId = eCodecId;
    //ALOGD("m_eCodecId=%d", eCodecId);
#ifdef LOCAL_SOCK_INPUT
	/* Local socket identity checking */
	if( (NULL != pcIdentity) ){
		snprintf(m_cIdentity, LSOCK_ID_LENGTH, "%s", pcIdentity);
	}else{
		int nSMSSCnt = 0;
		pthread_mutex_lock(&gMutex);
		nSMSSCnt = gSMSSCnt++;
		pthread_mutex_unlock(&gMutex);
		snprintf(m_cIdentity, LSOCK_ID_LENGTH, 
			"%s_ch%d", LOCAL_SOCKET_ID, nSMSSCnt);
	    ALOGD("%s:ID ",m_cIdentity);
	}
#else
	m_que = pstQue;
#endif /* LOCAL_SOCK_INPUT */
	m_ucFrmRate = ucFrmRate;
	//ALOGD("m_ucFrmRate = %d:ID ",m_ucFrmRate);
#ifdef LOCAL_SOCK_INPUT
	m_unSBufSize = unSBufSize;
	rtsp_db_info("m_unSBufSize=%d", m_unSBufSize);
	m_unRBufSize = unRBufSize;
	rtsp_db_info("m_unRBufSize=%d", m_unRBufSize);
#endif /* LOCAL_SOCK_INPUT */
	m_pDummyRTPSink = NULL;
	m_pSDPLine = NULL;
	m_cDoneFlag = 0;
    //ALOGD("%s:Out ",__func__);
}

VideoRTimeServerMediaSubsession::~VideoRTimeServerMediaSubsession()
{
	if ( (NULL != m_pSDPLine) ){delete[] m_pSDPLine;}
}

FramedSource *
	VideoRTimeServerMediaSubsession::createNewStreamSource(
	unsigned clientSessionId, unsigned & estBitrate)
{
	FramedSource *pctFrmSource = NULL;

    //ALOGD("m_eCodecId=%d",m_eCodecId);
	/* Check codec type */
	switch( (m_eCodecId) ){
	case VIDEO_FORMAT_H264:
		//estBitrate = 500; // kbps, estimate 
		pctFrmSource = 
			H264VideoStreamFramer::createNew(
				envir(), 
				new VideoRTimeFramedSource(
				envir(), 
#ifdef LOCAL_SOCK_INPUT
				m_cIdentity, 
#endif /* LOCAL_SOCK_INPUT */
				m_ucFrmRate, 
#ifdef LOCAL_SOCK_INPUT
				m_unSBufSize, 
				m_unRBufSize
#else
				m_que
#endif /* LOCAL_SOCK_INPUT */
				));
		break;
	case VIDEO_FORMAT_H265:
		//estBitrate = 500; // kbps, estimate
		pctFrmSource = 
			H265VideoStreamFramer::createNew(
				envir(), 
				new VideoRTimeFramedSource(
				envir(), 
#ifdef LOCAL_SOCK_INPUT
				m_cIdentity,
#endif /* LOCAL_SOCK_INPUT */
				m_ucFrmRate, 
#ifdef LOCAL_SOCK_INPUT
				m_unSBufSize, 
				m_unRBufSize
#else
				m_que
#endif /* LOCAL_SOCK_INPUT */
				));
		break;
	case VIDEO_FORMAT_MPEG4:
		//estBitrate = 500; // kbps, estimate
		pctFrmSource = 
			MPEG4VideoStreamFramer::createNew(
				envir(), 
				new VideoRTimeFramedSource(
				envir(),
#ifdef LOCAL_SOCK_INPUT
				m_cIdentity,
#endif /* LOCAL_SOCK_INPUT */
				m_ucFrmRate, 
#ifdef LOCAL_SOCK_INPUT
				m_unSBufSize, 
				m_unRBufSize
#else
				m_que
#endif /* LOCAL_SOCK_INPUT */
				));
		break;
	default:
    		//ALOGD("[%s]unsupport codec type",__func__);
		break;
	}
	return pctFrmSource;
}

RTPSink *VideoRTimeServerMediaSubsession::createNewRTPSink(
	Groupsock * rtpGroupsock, 
	unsigned char rtpPayloadTypeIfDynamic, 
	FramedSource * inputSource)
{
	RTPSink *pctRtpSink = NULL;
	/* Check codec type */
	switch( (m_eCodecId) ){
	case VIDEO_FORMAT_H264:
		pctRtpSink = H264VideoRTPSink::createNew(
			envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
		break;
	case VIDEO_FORMAT_H265:
		pctRtpSink = H265VideoRTPSink::createNew(
			envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
		break;
	case VIDEO_FORMAT_MPEG4:
		pctRtpSink = MPEG4ESVideoRTPSink::createNew(
			envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
		break;
	default:
    		//ALOGD("[%s]unsupport codec type",__func__);
		break;
	}
	return pctRtpSink;
}

static void afterPlayingDummy(void* clientData)
{
	VideoRTimeServerMediaSubsession* subsess = 
		(VideoRTimeServerMediaSubsession*)clientData;
	subsess->afterPlayingDummy1();
}

void VideoRTimeServerMediaSubsession::afterPlayingDummy1()
{
	/* Unschedule any pending 'checking' task */
	envir().taskScheduler().unscheduleDelayedTask(nextTask());
	/* Signal the event loop that we're done */
	setDoneFlag();
}

static void chkForAuxSDPLine(void * clientData)
{
	VideoRTimeServerMediaSubsession * subsess = 
		(VideoRTimeServerMediaSubsession *)clientData;
	subsess->chkForAuxSDPLine1();
}

void VideoRTimeServerMediaSubsession::chkForAuxSDPLine1()
{
	char const* dasl = NULL;
	if ( (NULL != m_pSDPLine) ){
		/* Signal the event loop that we're done */
		setDoneFlag();
	}else if ( (NULL != m_pDummyRTPSink) && ((dasl 
		= m_pDummyRTPSink->auxSDPLine()) != NULL) ){
		m_pSDPLine = strDup(dasl);
		m_pDummyRTPSink = NULL;
		/* Signal the event loop that we're done */
		setDoneFlag();
	}else if( (!m_cDoneFlag) ) {
#ifdef SCHED_NODELAY
		nextTask() = 
			envir().taskScheduler().scheduleDelayedTask(
			0, (TaskFunc*)chkForAuxSDPLine, this);
#else
		int to_delay = 100000;		// 100 ms
		if( (m_ucFrmRate > 0) ){
			to_delay = 1000000 / m_ucFrmRate;
		}
		nextTask() = 
			envir().taskScheduler().scheduleDelayedTask(
			to_delay, (TaskFunc*)chkForAuxSDPLine, this);
#endif /* SCHED_NODELAY */
	}
}

char const * VideoRTimeServerMediaSubsession::getAuxSDPLine(
	RTPSink * rtpSink, FramedSource * inputSource)
{
	/* It's already been set up (for a previous client) */
	if ( (NULL != m_pSDPLine) ){return m_pSDPLine;}

	if ( (NULL == m_pDummyRTPSink) ) {
		m_pDummyRTPSink = rtpSink;

		/* Start reading */
		m_pDummyRTPSink->startPlaying(
			*inputSource, afterPlayingDummy, this);
		//m_pDummyRTPSink->startPlaying(
		//	*inputSource, 0, 0);

		/* Check whether 
		     the sink's 'auxSDPLine()' is ready */
		chkForAuxSDPLine(this);
	}
	envir().taskScheduler().doEventLoop(&m_cDoneFlag);
	return m_pSDPLine;
}
